package externe.onglets;

//http://forum.java.sun.com/thread.jspa?threadID=337070&messageID=2865540 //
import java.awt.Color;
import java.awt.Component;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.JTabbedPane;
import javax.swing.JViewport;
import javax.swing.SwingUtilities;
import javax.swing.event.EventListenerList;
import javax.swing.plaf.basic.BasicTabbedPaneUI;

/**
 * A JTabbedPane which has a close ('X') icon on each tab.
 * 
 * To add a tab, use the method addTab(String, Component)
 * 
 * To have an extra icon on each tab (e.g. like in JBuilder, showing the file
 * type) use the method addTab(String, Component, Icon). Only clicking the 'X'
 * closes the tab.
 */
public class CloseableTabbedPane extends JTabbedPane
{
	public CloseableTabbedPane() {
		this(SwingUtilities.LEFT);
	}

	/**
	 * Creates a new instance of <code>CloseableTabbedPane</code>
	 * @param horizontalTextPosition the horizontal position of the text
	 * (e.g. SwingUtilities.TRAILING or SwingUtilities.LEFT)
	 */
	public CloseableTabbedPane(int horizontalTextPosition) {
		addMouseListener(new TabbedPaneMouseListener());
		addMouseMotionListener(new MouseMotionListener() {
			public void mouseDragged(MouseEvent e) {
				processMouseEvents(e);
			}
			public void mouseMoved(MouseEvent e) {
				processMouseEvents(e);
			}
		});
		setUI(new CloseableTabbedPaneUI(horizontalTextPosition));
	}

	/**
	 * Allows setting own closeicons.
	 * @param normal the normal closeicon
	 * @param hover the closeicon when the mouse is over
	 * @param pressed the closeicon when the mouse is pressed
	 */
	public void setCloseIcons(Icon normal, Icon hover, Icon pressed) {
		normalCloseIcon = normal;
		hoverCloseIcon = hover;
		pressedCloseIcon = pressed;
	}

	/**
	 * Adds a <code>Component</code> represented by a title and no icon.
	 * @param title the title to be displayed in this tab
	 * @param component the component to be displayed when this tab is clicked
	 */
	public void addTab(String title, Component component) {
		addTab(title, component, null);
	}

	/**
	 * Adds a <code>Component</code> represented by a title and an icon.
	 * @param title the title to be displayed in this tab
	 * @param component the component to be displayed when this tab is clicked
	 * @param extraIcon the icon to be displayed in this tab
	 */
	public void addTab(String title, Component component, Icon extraIcon) {
		boolean doPaintCloseIcon = true;
		Object prop = null;
		if ((prop = ((JComponent) component).getClientProperty("isClosable")) != null)
			doPaintCloseIcon = (Boolean) prop;

		component.addPropertyChangeListener("isClosable",
				new PropertyChangeListener() {
					public void propertyChange(PropertyChangeEvent e) {
						Object newVal = e.getNewValue();
						int index = -1;
						if (e.getSource() instanceof Component)
							index = indexOfComponent((Component) e.getSource());
						if (index != -1 && newVal != null && newVal instanceof Boolean)
							setCloseIconVisibleAt(index, (Boolean) newVal);
					}
				});

		super.addTab(title, doPaintCloseIcon ? new CloseTabIcon(extraIcon)
				: null, component);

		if (headerViewport == null)
			for (Component c : getComponents())
				if ("TabbedPane.scrollableViewport".equals(c.getName()))
					headerViewport = (JViewport) c;
	}

	/**
	 * Sets the closeicon at <code>index</code>.
	 * @param index the tab index where the icon should be set
	 * @param icon the icon to be displayed in the tab
	 * @throws IndexOutOfBoundsException if index is out of range (index < 0 || index >= tab count)
	 */
	private void setCloseIconVisibleAt(int index, boolean iconVisible)
			throws IndexOutOfBoundsException {
		super.setIconAt(index, iconVisible ? new CloseTabIcon(null) : null);
	}

	/**
	 * Processes all caught <code>MouseEvent</code>s.
	 * @param e the <code>MouseEvent</code>
	 */
	private void processMouseEvents(MouseEvent e) {
		int tabNumber = getUI().tabForCoordinate(this, e.getX(), e.getY());
		if (tabNumber < 0)
			return;

		boolean otherWasOver = false;
		CloseTabIcon ic;
		for (int i = 0; i < getTabCount(); ++i)
			if (i != tabNumber && (ic = (CloseTabIcon)getIconAt(i)) != null && ic.mouseover)
				ic.mouseover = !(otherWasOver = true);

		if (otherWasOver)
			repaint();

		CloseTabIcon icon = (CloseTabIcon) getIconAt(tabNumber);
		if (icon != null) {
			Rectangle rect = icon.getBounds();
			boolean vpIsNull = headerViewport == null;
			Point pos = vpIsNull ? new Point() : headerViewport
					.getViewPosition();
			int vpDiffX = (vpIsNull ? 0 : headerViewport.getX());
			int vpDiffY = (vpIsNull ? 0 : headerViewport.getY());
			Rectangle drawRect = new Rectangle(rect.x - pos.x + vpDiffX, rect.y
					- pos.y + vpDiffY, rect.width, rect.height);

			if (e.getID() == MouseEvent.MOUSE_PRESSED) {
				icon.mousepressed = e.getModifiers() == MouseEvent.BUTTON1_MASK;
				repaint(drawRect);
			} else if (e.getID() == MouseEvent.MOUSE_MOVED
					|| e.getID() == MouseEvent.MOUSE_DRAGGED
					|| e.getID() == MouseEvent.MOUSE_CLICKED) {
				pos.x += e.getX() - vpDiffX;
				pos.y += e.getY() - vpDiffY;
				if (rect.contains(pos)) {
					if (e.getID() == MouseEvent.MOUSE_CLICKED) {
						int selIndex = getSelectedIndex();
						if (fireCloseTab(selIndex)) {
							if (selIndex > 0) {
								// to prevent uncatchable null-pointers
								Rectangle rec = getUI().getTabBounds(this,
										selIndex - 1);

								MouseEvent event = new MouseEvent((Component) e
										.getSource(), e.getID() + 1, System
										.currentTimeMillis(), e.getModifiers(),
										rec.x, rec.y, e.getClickCount(), e
												.isPopupTrigger(), e
												.getButton());
								dispatchEvent(event);
							}
							// the tab is being closed
							// removeTabAt(tabNumber);
//							buddyList.imClosed(getTitleAt(selIndex));
							remove(selIndex);

						} else {
							icon.mouseover = false;
							icon.mousepressed = false;
							repaint(drawRect);
						}
					} else {
						icon.mouseover = true;
						icon.mousepressed = e.getModifiers() == MouseEvent.BUTTON1_MASK;
					}
				} else {
					icon.mouseover = false;
				}
				repaint(drawRect);
			}
		}
	}

	/**
	 * Adds an <code>CloseableTabbedPaneListener</code> to the tabbedpane.
	 * @param l the <code>CloseableTabbedPaneListener</code> to be added
	 */
	public void addCloseableTabbedPaneListener(CloseableTabbedPaneListener l) {
		listenerList.add(CloseableTabbedPaneListener.class, l);
	}

	/**
	 * Removes an <code>CloseableTabbedPaneListener</code> from the
	 * tabbedpane.
	 * @param l the listener to be removed
	 */
	public void removeCloseableTabbedPaneListener(CloseableTabbedPaneListener l) {
		listenerList.remove(CloseableTabbedPaneListener.class, l);
	}

	/**
	 * Returns an array of all the <code>SearchListener</code>s added to this
	 * <code>SearchPane</code> with addSearchListener().
	 * @return all of the <code>SearchListener</code>s added or an empty
	 *         array if no listeners have been added
	 */
	public CloseableTabbedPaneListener[] getCloseableTabbedPaneListener() {
		return listenerList.getListeners(CloseableTabbedPaneListener.class);
	}

	/**
	 * Notifies all listeners that have registered interest for notification on
	 * this event type.
	 * @param tabIndexToClose the index of the tab which should be closed
	 * @return true if the tab can be closed, false otherwise
	 */
	protected boolean fireCloseTab(int tabIndexToClose) {
		// Guaranteed to return a non-null array
		for (Object i : listenerList.getListenerList())
			if (i instanceof CloseableTabbedPaneListener &&
			  !((CloseableTabbedPaneListener)i).closeTab(tabIndexToClose))
				return false;
		return true;
	}

	/**
	 * The class which generates the 'X' icon for the tabs. The constructor
	 * accepts an icon which is extra to the 'X' icon, so you can have tabs like
	 * in JBuilder. This value is null if no extra icon is required.
	 */
	class CloseTabIcon implements Icon {
		/**
		 * Creates a new instance of <code>CloseTabIcon</code>
		 * @param fileIcon the additional fileicon, if there is one set
		 */
		public CloseTabIcon(Icon fileIcon) {
			this.fileIcon = fileIcon;
		}

		/**
		 * Draw the icon at the specified location. Icon implementations may use
		 * the Component argument to get properties useful for painting, e.g.
		 * the foreground or background color.
		 * @param c the component which the icon belongs to
		 * @param g the graphic object to draw on
		 * @param x the upper left point of the icon in the x direction
		 * @param y the upper left point of the icon in the y direction
		 */
		public void paintIcon(Component c, Graphics g, int x, int y) {
			boolean doPaintCloseIcon = true;
			// JComponent.putClientProperty("isClosable", new
			// Boolean(false));
			JTabbedPane tabbedpane = (JTabbedPane) c;
			int tabNumber = tabbedpane.getUI().tabForCoordinate(tabbedpane, x, y);
			JComponent curPanel = (JComponent) tabbedpane.getComponentAt(tabNumber);
			Object prop = null;
			if ((prop = curPanel.getClientProperty("isClosable")) != null)
				doPaintCloseIcon = (Boolean) prop;

			if (doPaintCloseIcon) {
				x_pos = x;
				y_pos = y;
				int y_p = y + 1;

				if (normalCloseIcon != null && !mouseover)
					normalCloseIcon.paintIcon(c, g, x, y_p);
				else if (hoverCloseIcon != null && mouseover && !mousepressed)
					hoverCloseIcon.paintIcon(c, g, x, y_p);
				else if (pressedCloseIcon != null && mousepressed)
					pressedCloseIcon.paintIcon(c, g, x, y_p);
				else {
					y_p++;

					Color col = g.getColor();

					if (mousepressed && mouseover) {
						g.setColor(Color.WHITE);
						g.fillRect(x + 1, y_p, 12, 13);
					}

					g.setColor(Color.black);
					g.drawLine(x + 1, y_p, x + 12, y_p);
					g.drawLine(x + 1, y_p + 13, x + 12, y_p + 13);
					g.drawLine(x, y_p + 1, x, y_p + 12);
					g.drawLine(x + 13, y_p + 1, x + 13, y_p + 12);
					g.drawLine(x + 3, y_p + 3, x + 10, y_p + 10);
					if (mouseover)
						g.setColor(Color.GRAY);
					g.drawLine(x + 3, y_p + 4, x + 9, y_p + 10);
					g.drawLine(x + 4, y_p + 3, x + 10, y_p + 9);
					g.drawLine(x + 10, y_p + 3, x + 3, y_p + 10);
					g.drawLine(x + 10, y_p + 4, x + 4, y_p + 10);
					g.drawLine(x + 9, y_p + 3, x + 3, y_p + 9);
					g.setColor(col);
					if (fileIcon != null) {
						fileIcon.paintIcon(c, g, x + width, y_p);
					}
				}
			}
		}

		public int getIconWidth() {
			return width + (fileIcon != null ? fileIcon.getIconWidth() : 0);
		}

		public int getIconHeight() {
			return height;
		}

		public Rectangle getBounds() {
			return new Rectangle(x_pos, y_pos, width, height);
		}

		private int x_pos;
		private int y_pos;
		private boolean mouseover = false;
		private boolean mousepressed = false;
		private final int width = 16;
		private final int height = 16;
		private final Icon fileIcon;
	}

	/**
	 * A specific <code>BasicTabbedPaneUI</code>.
	 */
	private class CloseableTabbedPaneUI extends BasicTabbedPaneUI
	{
		/**
		 * Creates a new instance of <code>CloseableTabbedPaneUI</code>
		 */
		private CloseableTabbedPaneUI() {
			this(SwingUtilities.LEFT);
		}

		/**
		 * Creates a new instance of <code>CloseableTabbedPaneUI</code>
		 * @param horizontalTextPosition the horizontal position of the text
		 * (e.g. SwingUtilities.TRAILING or SwingUtilities.LEFT)
		 */
		private CloseableTabbedPaneUI(int horizontalTextPosition) {
			this.horizontalTextPosition = horizontalTextPosition;
		}

		/**
		 * Lays out the label
		 * @param tabPlacement the placement of the tabs
		 * @param metrics the font metrics
		 * @param tabIndex the index of the tab
		 * @param title the title of the tab
		 * @param icon the icon of the tab
		 * @param tabRect the tab boundaries
		 * @param iconRect the icon boundaries
		 * @param textRect the text boundaries
		 * @param isSelected true whether the tab is selected, false otherwise
		 */
		protected void layoutLabel(int tabPlacement, FontMetrics metrics,
				int tabIndex, String title, Icon icon, Rectangle tabRect,
				Rectangle iconRect, Rectangle textRect, boolean isSelected) {

			textRect.x = textRect.y = iconRect.x = iconRect.y = 0;

			javax.swing.text.View v = getTextViewForTab(tabIndex);
			if (v != null)
				tabPane.putClientProperty("html", v);

			SwingUtilities.layoutCompoundLabel((JComponent) tabPane, metrics,
					title, icon, SwingUtilities.CENTER, SwingUtilities.CENTER,
					SwingUtilities.CENTER,
					// SwingUtilities.TRAILING,
					horizontalTextPosition, tabRect, iconRect, textRect,
					textIconGap + 2);

			tabPane.putClientProperty("html", null);

			int xNudge = getTabLabelShiftX(tabPlacement, tabIndex, isSelected);
			int yNudge = getTabLabelShiftY(tabPlacement, tabIndex, isSelected);
			iconRect.x += xNudge;
			iconRect.y += yNudge;
			textRect.x += xNudge;
			textRect.y += yNudge;
		}

		private final int horizontalTextPosition;
	}

	private class TabbedPaneMouseListener extends java.awt.event.MouseAdapter
	{
		public void mouseClicked(MouseEvent e) {
			processMouseEvents(e);
		}

		public void mouseExited(MouseEvent e) {
			CloseTabIcon icon;
			for (int i = 0; i < getTabCount(); ++i)
				if ((icon = (CloseTabIcon)getIconAt(i)) != null)
					icon.mouseover = false;
			repaint();
		}

		public void mousePressed(MouseEvent e) {
			processMouseEvents(e);
		}
	}

	/** The viewport of the scrolled tabs. */
	private JViewport headerViewport = null;
	private Icon normalCloseIcon = null;
	private Icon hoverCloseIcon = null;
	private Icon pressedCloseIcon = null;
	private final EventListenerList listenerList = new EventListenerList();
}
